package com.orion.config.security;

import com.orion.config.token.JwtProperties;
import com.orion.domain.SysUser;
import com.orion.entity.Result;
import com.orion.enumeration.CommonCodeEnum;
import com.orion.enumeration.TokenCodeEnum;
import com.orion.util.JwtUtil;
import com.orion.util.RedisUtil;
import com.orion.util.ResponseUtil;
import io.jsonwebtoken.Claims;
import org.apache.commons.lang.StringUtils;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * @author Administrator
 * @date 2021/11/11
 */
@Component
public class RefreshTokenWebFilter implements WebFilter {

    @Resource(name = "jwtProperties")
    private JwtProperties jwtProperties;

    @Resource(name = "authReactiveUserDetailService")
    private ReactiveUserDetailsService authReactiveUserDetailService;

    @Resource(name = "passwordEncoder")
    private PasswordEncoder passwordEncoder;

    private Pattern pattern = Pattern.compile("/orionRefresh");

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        System.out.println("in RefreshTokenWebFilter....");
        ServerHttpRequest serverHttpRequest = exchange.getRequest();
        ServerHttpResponse serverHttpResponse = exchange.getResponse();

        if (pattern.matcher(serverHttpRequest.getURI().getRawPath()).matches()) {
            if (!HttpMethod.POST.matches(serverHttpRequest.getMethodValue())) {
                Result r = CommonCodeEnum.COMMON_INVALID_REQUEST.toResult();
                return ResponseUtil.jsonResp(serverHttpResponse, r);
            }

            String refreshToken = serverHttpRequest.getHeaders().getFirst("r-token");
            String username = serverHttpRequest.getHeaders().getFirst("username");
            String password = serverHttpRequest.getHeaders().getFirst("password");

            Result r;

            if (StringUtils.isBlank(refreshToken) || StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
                r = CommonCodeEnum.COMMON_MISSING_PARAM.toResult();
                return ResponseUtil.jsonResp(serverHttpResponse, r);
            }

            Claims claims = JwtUtil.parseToken(refreshToken, jwtProperties.getRefreshSalt());
            System.out.println("claims in RefreshTokenWebFilter : " + claims);

            String usernameInToken = claims.get("username", String.class);

            if (!usernameInToken.equals(username)) {
                r = TokenCodeEnum.TOKEN_INVALID.toResult();
                return ResponseUtil.jsonResp(serverHttpResponse, r);
            }

           /* return authReactiveUserDetailService.findByUsername(username).cast(SysUser.class)
                    .filter(u -> this.passwordEncoder.matches(password, u.getPassword()))
                    .switchIfEmpty(Mono.defer(() -> Mono.from(ResponseUtil.jsonResp(serverHttpResponse, TokenCodeEnum.TOKEN_INVALID.toResult()))))
                    .filter(u -> {
                        String refreshKey = String.join(":", .toString(), u.g, "refresh");
                        Boolean exists = RedisUtil.exists(refreshKey);
                        return exists;
                    })
                    .switchIfEmpty(Mono.just(ResponseUtil.jsonResp(serverHttpResponse,TokenCodeEnum.TOKEN_EXPIRED.toResult())))
                                    .flatMap()
*/

            Mono<UserDetails> byUsername = authReactiveUserDetailService.findByUsername(username);
            // 将用户信息存入 authentication，方便后续校验
            SysUser sysUser  = (SysUser) byUsername.block();
            if (!passwordEncoder.matches(password,sysUser.getPassword())){
                System.out.println("password not match...");
                r = CommonCodeEnum.COMMON_SERVER_ERROR.toResult();
                return ResponseUtil.jsonResp(serverHttpResponse, r);
            }

            Map<String, Object> coverClaims = new HashMap<>(16);
            coverClaims.put("id", sysUser.getId());
            coverClaims.put("username", sysUser.getUsername());
            if (!sysUser.getAuthorities().isEmpty()) {
                List<String> authorities = new ArrayList<>();
                for (GrantedAuthority grantedAuthority : sysUser.getAuthorities()) {
                    authorities.add(grantedAuthority.getAuthority());
                }
                coverClaims.put("authorities", authorities);
            }

            System.out.println("rebuild accessToken and refreshToken...");


            r = CommonCodeEnum.COMMON_SUCCESS.toResult();
            //重新生成token
            //生成accessToken.refreshToken复用
            String accessToken = JwtUtil.createToken(coverClaims, jwtProperties.getAccessSalt());

            String accessKey = String.join(":", sysUser.getId().toString(), username, "access");
            RedisUtil.setEx(accessKey, accessToken, jwtProperties.getAccessEx());

            serverHttpResponse.getHeaders().add("a-token", accessToken);

            return ResponseUtil.jsonResp(serverHttpResponse, r);
        }
        return chain.filter(exchange);
    }
}
